﻿' ******************************************************************************
' ** 
' **  Yahoo! Managed
' **  Written by Marius Häusler 2011
' **  It would be pleasant, if you contact me when you are using this code.
' **  Contact: YahooFinanceManaged@gmail.com
' **  Project Home: http://code.google.com/p/yahoo-finance-managed/
' **  
' ******************************************************************************
' **  
' **  Copyright 2011 Marius Häusler
' **  
' **  Licensed under the Apache License, Version 2.0 (the "License");
' **  you may not use this file except in compliance with the License.
' **  You may obtain a copy of the License at
' **  
' **    http://www.apache.org/licenses/LICENSE-2.0
' **  
' **  Unless required by applicable law or agreed to in writing, software
' **  distributed under the License is distributed on an "AS IS" BASIS,
' **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
' **  See the License for the specific language governing permissions and
' **  limitations under the License.
' ** 
' ******************************************************************************


Namespace Base

    ''' <summary>
    ''' Provides methods and properties for downloading data and controlling these download processes. This class must be inherited.
    ''' </summary>
    ''' <remarks>This class can not be instanced without inheritation</remarks>
    Public MustInherit Class Download
        Implements IDownloadClient

        Protected Event AsyncStreamDownloadCompleted(ByVal sender As Download, ByVal e As StreamDownloadCompletedEventArgs)

        Friend ReadOnly mHelper As New YahooManaged.MyHelper
        Private ReadOnly mWebClients As New List(Of TimeoutWebClient)
        Private mProxy As IWebProxy = WebProxy.GetDefaultProxy
        Private mTimeout As Integer = 30000
        Private mGui As System.Windows.Forms.Control

        ''' <summary>
        ''' Gets the user arguments of each download process.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>If you just need a single object, you should use UserArgs(Int32) property, because of higher performance.</remarks>
        Public ReadOnly Property UserArgs() As Object()
            Get
                If mWebClients.Count > 0 Then
                    Dim lst(mWebClients.Count - 1) As Object
                    For i As Integer = 0 To mWebClients.Count - 1
                        Dim ea As DownloadEventArgs = TryCast(mWebClients(i).UserArgs, DownloadEventArgs)
                        If ea IsNot Nothing Then
                            lst(i) = ea.UserArgs
                        Else
                            lst(i) = mWebClients(i).UserArgs
                        End If
                    Next
                    Return lst
                Else
                    Return New Object() {}
                End If
            End Get
        End Property
        ''' <summary>
        ''' Gets the user argument of a download process at a specific index position.
        ''' </summary>
        ''' <param name="index">The index position of the download</param>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property UserArgs(ByVal index As Integer) As Object
            Get
                If index < 0 Or index > mWebClients.Count - 1 Then
                    Throw New IndexOutOfRangeException("The index is out of download list range.")
                Else
                    Return Me.GetDeeperObject(mWebClients(index).UserArgs)
                End If
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the time in miliseconds when the download will cancel if the time reached before it is completed.
        ''' </summary>
        ''' <value>Timeout in miliseconds. If value is '-1', the next download wont have a timeout.</value>
        ''' <returns>The timeout in miliseconds.</returns>
        ''' <remarks></remarks>
        Public Property Timeout() As Integer
            Get
                Return mTimeout
            End Get
            Set(ByVal value As Integer)
                If value >= 0 Then : mTimeout = value
                Else : mTimeout = 0
                End If
            End Set
        End Property
        ''' <summary>
        ''' Gets or sets the used proxy setting for download.
        ''' </summary>
        ''' <value></value>
        ''' <returns>The actual setted proxy</returns>
        ''' <remarks>Default value are proxy settings from Internet Explorer/Windows Internet Settings.</remarks>
        Public Property Proxy() As IWebProxy
            Get
                Return mProxy
            End Get
            Set(ByVal value As IWebProxy)
                mProxy = value
            End Set
        End Property
        ''' <summary>
        ''' Gets the number of actually running asynchronous downloads.
        ''' </summary>
        ''' <value></value>
        ''' <returns>The number of active downloads</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property AsyncDownloadsCount() As Integer
            Get
                Return mWebClients.Count
            End Get
        End Property
        Public Property FromAsyncToGuiThread As Boolean
            Get
                Return mGui IsNot Nothing
            End Get
            Set(ByVal value As Boolean)
                If Me.FromAsyncToGuiThread And value = False Then
                    mGui = Nothing
                ElseIf Me.FromAsyncToGuiThread = False And value Then
                    mGui = New System.Windows.Forms.Control
                    mGui.CreateControl()
                End If
            End Set
        End Property
        Protected Sub New()
            Me.FromAsyncToGuiThread = True
        End Sub

        Protected Function DownloadStream(ByVal url As String, Optional ByVal additionalHeaders() As KeyValuePair(Of HttpRequestHeader, String) = Nothing) As StreamResponse
            Using wc As New TimeoutWebClient(mGui, mTimeout)
                If mProxy IsNot Nothing Then wc.Proxy = mProxy
                Return wc.Download(url, additionalHeaders)
            End Using
        End Function
        Protected Sub DownloadStreamAsync(ByVal url As String, ByVal args As Object, Optional ByVal additionalHeaders() As KeyValuePair(Of HttpRequestHeader, String) = Nothing)
            Dim wc As New TimeoutWebClient(mGui, mTimeout)
            If mProxy IsNot Nothing Then wc.Proxy = mProxy
            AddHandler wc.AsyncDownloadCompleted, AddressOf Me.AsyncDownload_Completed
            mWebClients.Add(wc)
            wc.DownloadAsync(url, args, additionalHeaders)
        End Sub

        ''' <summary>
        ''' Cancels all running downloads.
        ''' </summary>
        ''' <returns>TRUE, if all downloads were canceled. FALSE, if something goes wrong.</returns>
        ''' <remarks></remarks>
        Public Function CancelAsyncAll() As Boolean
            If mWebClients.Count > 0 Then
                For i As Integer = mWebClients.Count - 1 To 0 Step -1
                    mWebClients(i).CancelAsync()
                Next
            End If
            Return mWebClients.Count = 0
        End Function
        ''' <summary>
        ''' Cancels all running downloads containing the passed user argument.
        ''' </summary>
        ''' <param name="userArgs">The user argument that the download object contains.</param>
        ''' <returns>The number of canceled downloads.</returns>
        ''' <remarks></remarks>
        Public Function CancelAsync(ByVal userArgs As Object) As Integer
            If mWebClients.Count > 0 Then
                Dim count As Integer = 0
                For i As Integer = mWebClients.Count To 0 Step -1
                    If Me.DeepUserArgsEqual(mWebClients(i).UserArgs, userArgs) Then
                        mWebClients(i).CancelAsync()
                        count += 1
                    End If
                Next
                Return count
            Else
                Return 0
            End If
        End Function
        ''' <summary>
        ''' Cancels a running download at specific index position.
        ''' </summary>
        ''' <param name="index">The index position of the download process in queue.</param>
        ''' <remarks></remarks>
        Public Sub CancelAsyncAt(ByVal index As Integer)
            mWebClients(index).CancelAsync()
        End Sub
        ''' <summary>
        ''' Proves if the download processes contains the passed user argument.
        ''' </summary>
        ''' <param name="userArgs">Individual user argument that the download contains.</param>
        ''' <returns>The number of download processes containing the passed user argument.</returns>
        ''' <remarks></remarks>
        Public Function Contains(ByVal userArgs As Object) As Integer
            Dim count As Integer = 0
            For Each wc As TimeoutWebClient In mWebClients
                If Me.DeepUserArgsEqual(wc.UserArgs, userArgs) Then count += 1
            Next
            Return count
        End Function

        Private Sub AsyncDownload_Completed(ByVal sender As TimeoutWebClient, ByVal ea As StreamDownloadCompletedEventArgs)
            RemoveHandler sender.AsyncDownloadCompleted, AddressOf AsyncDownload_Completed
            If mWebClients.Contains(sender) Then mWebClients.Remove(sender)
            RaiseEvent AsyncStreamDownloadCompleted(Me, ea)
            sender.Dispose()
        End Sub

        Private Function DeepUserArgsEqual(ByVal argsX As Object, ByVal argsY As Object) As Boolean
            Return Me.GetDeeperObject(argsX) Is Me.GetDeeperObject(argsY)
        End Function
        Private Function GetDeeperObject(ByVal args As Object) As Object
            Dim deeperObj As Object = Nothing
            If args IsNot Nothing AndAlso TypeOf args Is DownloadEventArgs Then : deeperObj = DirectCast(args, DownloadEventArgs).UserArgs
            Else : deeperObj = args
            End If
            If deeperObj IsNot Nothing AndAlso TypeOf deeperObj Is DownloadEventArgs Then deeperObj = Me.GetDeeperObject(deeperObj)
            Return deeperObj
        End Function

    End Class



    Public Class StreamDownloadCompletedEventArgs
        Inherits DownloadCompletedEventArgs
        Public Overloads ReadOnly Property Response() As StreamResponse
            Get
                Return DirectCast(MyBase.Response, StreamResponse)
            End Get
        End Property
        Friend Sub New(ByVal userArgs As Object, ByVal response As StreamResponse)
            MyBase.New(userArgs, response)
        End Sub

    End Class


    Public Class StreamResponse
        Inherits Response
        Public Overloads ReadOnly Property Result() As IO.MemoryStream
            Get
                Return DirectCast(MyBase.Result, IO.MemoryStream)
            End Get
        End Property
        Friend Sub New(ByVal info As ConnectionInfo, ByVal result As IO.MemoryStream)
            MyBase.New(info, result)
        End Sub
    End Class




End Namespace
